// Copyright (c) 2023 刻BITTER
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.


#include <Arduino.h>
#include <Wire.h>
#include <stdlib.h>

// freertos
#include <Arduino_FreeRTOS.h>

// os_basic
#include "os_basic_mutex.hpp"
#include "os_basic_task.hpp"

namespace os = os_basic;

// ioxx
#include "pin/avr_arduino.hpp"

// mega128_specific
#include "mega128_specific.hpp"

namespace m128 = mega128_specific;

const ioxx::Pin RGB_PIN = m128::RGB_PIN;
constexpr uint8_t RGB_PIN_NUM = m128::RGB_PIN;
;

const ioxx::Pin LED_PIN = m128::LED_PIN;

// oled_basic_v2
#include "oled_backend.hpp"
#include "oled_basic_font.hpp"
#include "oled_basic_v2.hpp"
#include "oled_ssd13xx.hpp"
#include "oled_print.hpp"

namespace ob = oled_basic;

// hexpro
#include "hex_protocol/hex_protocol.hpp"

// RGB
#include <FastLED.h>

#define RGB_COUNT      3
#define COLOR_ORDER    GRB
#define LED_TYPE       WS2812
#define RGB_BRIGHTNESS 255


/////////////////////// TOP LEVEL GLOBALS /////////////////////////


// U8G2_SSD1306_128X64_NONAME_1_HW_I2C u8g2(U8G2_R0, /* reset=*/U8X8_PIN_NONE);

ob::BackendWireSSD13xx oled_backend;
constexpr auto &oled_display = ob::SSD1306_12864_WIRE;
ob::MonoBasic obmb{oled_backend, oled_display, ob::font::ascii_8x16};
ob::MonoPrint obp{obmb};


/////////////////////// RGB TASK /////////////////////////

/**
 * @brief RGB 动画驱动任务
 *
 */
class RgbTask : public os::Task<RgbTask, os::MIN_STACK_DEPTH + 96> {
   private:
    CRGB _rgb[RGB_COUNT];

   public:
    void run() {
        // SETUP

        // tell FastLED about the LED strip configuration
        FastLED.addLeds<LED_TYPE, RGB_PIN_NUM, COLOR_ORDER>(_rgb, RGB_COUNT);
        // FastLED.setCorrection(TypicalSMD5050);
        // set master brightness control
        // FastLED.setBrightness(RGB_BRIGHTNESS);

        pinMode(RGB_PIN_NUM, OUTPUT);

        // 这堆代码都用来生成RGB 流水动画，凑合能用，不要尝试去理解
        constexpr uint8_t DELTA = 6;
        constexpr uint8_t PEAK = 90;
        constexpr uint8_t MAX = 70;
        constexpr uint8_t RED = 120;

        CRGB color{RED, 10, 10};
        int8_t direction = 0 - DELTA;
        uint8_t base_g = MAX;

        fill_solid(_rgb, RGB_COUNT, color);

        // LOOP

        while (true) {
            int8_t dir = direction;
            base_g += direction;
            uint8_t g = base_g;
            uint8_t output_g = g;

            for (uint8_t i = 0; i < RGB_COUNT; ++i) {
                // 这部分代码主要用来生成三角波，
                // g 在base_g 的基础上沿着三角波方向推进RGB_COUNT
                // 当g 遇到上下限时增量方向换向
                // 如果峰值位置是在灯带中间，在扫描灯带的过程中，增量方向会在中间换向，比如，g 是先增加，后减少，
                // 那么下一次扫描，base_g 还处在增加的方向。直到第一个灯正好在峰值处，则整个灯带的方向都变成减少，
                // base_g 也开始减少，所以base_g 的方向direction 和g 的方向在此时同步
                if (g < DELTA) {
                    dir = DELTA;
                }
                else if (g > PEAK) {
                    dir = 0 - DELTA;
                }

                if (dir != direction) {
                    if (i == 0) {
                        direction = dir;
                    }
                }

                if (g > MAX) {
                    output_g = 0;
                }
                else {
                    output_g = MAX - g;
                }

                // DEBUG，可以用串口看看三角波具体长什么样，实际波形是锯齿形三角波，
                //  相当于在三角波基频上又叠加了高频锯齿
                //  Serial.print(output_g);
                //  Serial.print('\n');

                _rgb[i].r = constrain(RED + g / 2, RED, 255);
                _rgb[i].g = output_g / 3;
                _rgb[i].b = output_g;

                g += dir;
            }


            do {
                // 用GPIO 方式驱动WS2812，不能被中断
                // os::TaskCriticalScope tcs;
                // FastLED 内部会关中断，不用外面处理

                FastLED.show();
            } while (0);

            vTaskDelay(os::ms(200));
        }
    }
};


///////////////////////  TASK    ////////////////////////////


/**
 * @brief 主任务（线程）的实现，负责调度全局资源和交互
 *
 */
class MainTask : public os::Task<MainTask, 512> {
   private:
   public:
    MainTask() {}

    void run() {
        // SETUP
        pinMode(LED_PIN, OUTPUT);

        oled_backend.init(true);
        oled_backend.init_display(oled_display);

        obmb.clear_screen();
        obmb.set_cursor(20, 0);
        obmb.put_str("OLED BASIC ");
        obmb.put_str("V2", ob::ink::inverse);
        
        obmb.set_num_base(ob::number_base::n10);
        obmb.enable_number_right_align(4);

        hexpro::ProtocolHandler hex{Serial};

        int8_t a = 0;
        int8_t b = 0;

        // LOOP

        hex.begin_session();

        while (true) {
            LED_PIN.toggle();

            obmb.set_cursor(10, 4);
            obmb.put_signed(a, ob::ink::inverse);
            obmb.set_cursor(10, 6);
            obmb.put_signed(b);

            ++a;
            --b;

            if (hex.rx_available()) {
                bool r = hex.rx();
                if (r) {
                    Serial.println("Type: ");
                    Serial.println(hex.rx_frame_type());
                }
                else {
                    Serial.println("Err: ");
                    Serial.println(static_cast<uint8_t>(hex.last_error()));
                }

                Serial.print(": ");
                Serial.print(hex.rx_data(0));
                Serial.print(", ");
                Serial.print(hex.rx_data(1));
                Serial.print("\r\n");
            }

            vTaskDelay(os::ms(500));
        }
    }
};


///////////////////////  MAIN    ////////////////////////////


MainTask main_task;
RgbTask rgb_task;

void os_setup() {
    main_task.create(os::MIDDLE_PRIORITY, nullptr);
    rgb_task.create(os::LOWEST_PRIORITY, nullptr);
}


void setup() {
    Wire.begin();

    Serial.begin(115200);

    os_setup();

    // =========== OS，启动 ===========

    vTaskStartScheduler();

    while (1) {
        // 调度器不应该退出
    }
}


void loop() { /* NO USE */ }
